// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#pragma once

#include <cantor/parser/ast/base.h>
#include <cantor/parser/ast/dcl.h>
#include <cantor/parser/ast/ddl.h>
#include <cantor/parser/ast/dql.h>
#include <cantor/parser/ast/dml.h>

namespace cantor::parser {
    ////////////////////////////////////////////////////////////////////////////////
    /// StmtNode - Represents a SQL statement node in the AST.
    ///
    /// This struct models an individual SQL statement, including its type and
    /// any associated data. It acts as a parent class for all specific SQL
    /// statements, such as SELECT, INSERT, CREATE, etc. The `stmt_type` field
    /// determines which type of SQL statement this node corresponds to.
    ///
    /// Example SQL:
    ///   SELECT * FROM my_table;  -- Corresponds to STMT_SELECT
    ///   CREATE TABLE my_table (id INT);  -- Corresponds to STMT_CREATE_TABLE
    ///   INSERT INTO my_table VALUES (1); -- Corresponds to STMT_INSERT
    ///
    /// The `stmt_type` field is used to distinguish between different statement types
    /// during parsing and execution.
    ////////////////////////////////////////////////////////////////////////////////

    struct StmtNode : public Node {
        StmtNode(pb::ast::StmtType s) : Node(pb::ast::NODE_TYPE_STMT), stmt_type(s) {
        }

        const pb::ast::StmtType stmt_type;

        nlohmann::ordered_json to_json_impl() const override;
    };


    ////////////////////////////////////////////////////////////////////////////////
    /// TypeStmt - Represents a type statement in the SQL language.
    ///
    /// This struct models SQL `CREATE TABLE` and type-related statements, including
    /// defining new data types, modifying existing ones, and specifying type-related
    /// metadata. It includes details like the type's name, its OID, bounds, and
    /// whether it refers to a set, array, or user-defined type.
    ///
    /// Examples below illustrate the various complex types and custom types that
    /// can be defined within `CREATE TABLE` statements.
    ///
    /// Example SQL 1: Create a table with a STRUCT (nested type):
    ///   CREATE TABLE employee (
    ///       id INT,  -- Simple integer column
    ///       name STRING(100),  -- STRING type with length of 100
    ///       address STRUCT(street STRING(100), city STRING(100), zip INT)  -- Nested structure
    ///   );
    ///   -- Here, `address` is a nested `STRUCT` that contains three fields: `street`, `city`, and `zip`.
    ///   -- This allows for complex, nested data in the same table row, making it suitable for
    ///   -- storing more structured data like addresses or geographical information.
    ///
    /// Example SQL 2: Create a table with an ENUM (user-defined type):
    ///   CREATE TABLE users (
    ///       id INT,
    ///       status ENUM('active', 'inactive', 'suspended')  -- User-defined ENUM type
    ///   );
    ///   -- In this example, the `status` column uses an `ENUM` type to restrict the possible values to
    ///   -- 'active', 'inactive', or 'suspended'. This is useful for fields that have a fixed set of
    ///   -- predefined values, like user status or product categories.
    ///
    /// Example SQL 3: Create a table with an ARRAY (list of elements):
    ///   CREATE TABLE orders (
    ///       order_id INT,
    ///       items STRING(255)[],  -- Array of strings, each item in the order
    ///       prices DECIMAL(10, 2)[]  -- Array of decimal values for item prices
    ///   );
    ///   -- In this example, the `items` column is an array of `STRING` values representing the names of items
    ///   -- in an order, while the `prices` column stores the prices for each item in the same order.
    ///   -- Arrays are useful for storing collections of similar items that need to be grouped together.
    ///
    /// Example SQL 4: Create a table with JSON type (flexible structure):
    ///   CREATE TABLE products (
    ///       product_id INT,
    ///       specifications JSON  -- Flexible JSON data to store product specs
    ///   );
    ///   -- The `specifications` column is a `JSON` type, which can store a flexible, structured document
    ///   -- such as a product's specifications, allowing for an arbitrary number of key-value pairs like
    ///   -- size, color, weight, etc. JSON is often used when the structure of the data is dynamic and
    ///   -- needs to be queried in a flexible way.
    ///
    /// Example SQL 5: Create a table with a MAP (key-value pairs):
    ///   CREATE TABLE configurations (
    ///       config_id INT,
    ///       settings MAP(STRING(100), STRING(255))  -- A map of key-value pairs
    ///   );
    ///   -- Here, `settings` is a `MAP` where each key is a `STRING(100)` and each value is a `STRING(255)`.
    ///   -- This type is useful for storing key-value pairs where each key corresponds to a configuration
    ///   -- setting (e.g., 'color' => 'red', 'font_size' => '14px').
    ///
    /// Example SQL 6: Create a table with an array of STRUCT (nested arrays):
    ///   CREATE TABLE students (
    ///       student_id INT,
    ///       subjects STRUCT(name STRING(100), grade INT)[]  -- Array of structured subjects with name and grade
    ///   );
    ///   -- The `subjects` column is an array of `STRUCT` types, where each `STRUCT` contains a `name` (subject name)
    ///   -- and a `grade` (grade achieved in that subject). This example shows how to store arrays of complex
    ///   -- structures, enabling flexible representations of nested data in tables.
    ///
    /// Example SQL 7: Create a table with a STRUCT and ARRAY of STRUCT:
    ///   CREATE TABLE orders (
    ///       order_id INT,
    ///       customer STRUCT(name STRING(100), contact STRING(100)),  -- Customer info as a STRUCT
    ///       items STRUCT(item_name STRING(100), quantity INT)[]  -- Array of STRUCT representing items in the order
    ///   );
    ///   -- In this case, the `customer` column is a `STRUCT` that holds the name and contact information
    ///   -- of the customer, while the `items` column is an array of `STRUCT` types where each item has a name
    ///   -- and quantity, representing the ordered items in the customer's order.
    ///
    /// The `typename_type` field determines the kind of type being defined or used.
    ////////////////////////////////////////////////////////////////////////////////

    struct TypeStmt : public StmtNode {
        TypeStmt(pb::ast::TypeNameType t = pb::ast::TYPE_NAME_NONE) : StmtNode(pb::ast::StmtType::STMT_TYPE_NAME),
                                                                  typename_type(t) {
        }

        ~TypeStmt() override = default;

        /// only one now, using vector
        /// for extern future
        /// List of names for the type, supporting future extensions.
        Vector<String> names;
        /// OID (Object Identifier) for the type.
        /// not using now
        uint32_t typeOid{0};
        /// Whether the type is a set.
        bool setof{false};
        /// Whether a %TYPE is specified.
        bool pct_type{false};

        /// Type modifiers (e.g., precision, scale).
        Vector<ConstantExpr *> typmods;

        /// Array bounds if applicable.
        /// unlimited is -1
        Vector<IntConstantExpr *> array_bounds;

        /// The specific type name type (e.g., STRUCT, ENUM, JSON).
        const pb::ast::TypeNameType typename_type;

        nlohmann::ordered_json to_json_impl() const override;

        void print() const override {
            std::cout << "name:[";
            bool first = true;
            for (int i = 0; i < names.size(); ++i) {
                if (!first) {
                    std::cout << ".";
                }
                first = false;
                std::cout << names[i];
            }
            std::cout << "]";
        }

        void to_stream(std::ostream &os) const override {
            os << "type_str:";
            bool first = true;
            for (int i = 0; i < names.size(); ++i) {
                if (!first) {
                    os << ".";
                }
                first = false;
                os << names[i];
            }
            os << ", type_enum: " << static_cast<int>(typename_type);
        }
    };

    inline std::ostream &operator<<(std::ostream &os, const TypeStmt &t) {
        t.to_stream(os);
        return os;
    }

    ////////////////////////////////////////////////////////////////////////////////
    /// TypeNameGeneric - Fallback for user-defined or generic types.
    ///
    /// This class represents a generic fallback type for situations where the SQL
    /// type doesn't fall under the standard predefined categories. It is typically
    /// used for user-defined types, types that aren't natively recognized by the
    /// system, or for cases where types are dynamically defined at runtime.
    ///
    /// Example usage:
    ///   CREATE TABLE my_table (
    ///       custom_col my_custom_type  -- A user-defined custom type
    ///   );
    ///
    /// This allows the database to handle custom types dynamically and flexibly.
    /// It also supports array types and type modifiers (like length or precision).
    ////////////////////////////////////////////////////////////////////////////////

    struct TypeNameGeneric final : public TypeStmt {
        TypeNameGeneric() : TypeStmt(pb::ast::TYPE_NAME_GENERIC) {
        }

        nlohmann::ordered_json to_json() const override;

        static TypeNameGeneric *make_typename(String name, turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameGeneric>(arena);
            ptr->names.push_back(name, arena);
            return ptr;
        }
    };


    struct TypeNameNumeric final : public TypeStmt {
        TypeNameNumeric() : TypeStmt(pb::ast::TYPE_NAME_NUMERIC) {
        }

        /// Static factory function to create a new TypeNameGeneric instance
        static TypeNameNumeric *make_typename(pb::ast::NumericType it, turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameNumeric>(arena);
            ptr->numeric_type = it;
            String s;
            s.strdup(type_name(it), arena);
            ptr->names.push_back(s, arena);
            return ptr;
        }


        static const char *type_name(pb::ast::NumericType t);

        nlohmann::ordered_json to_json() const override;

        pb::ast::NumericType numeric_type{pb::ast::NumericType::NT_NONE};
        static std::unordered_map<int, const char *> TYPE_NAME_MAP;
    };


    ////////////////////////////////////////////////////////////////////////////////
    /// TypeNameTimestamp - Represents the `TIMESTAMP` type in SQL with precision.
    ///
    /// This class models the `TIMESTAMP` type, including its precision (e.g.,
    /// seconds, milliseconds, microseconds, nanoseconds). It extends `TypeStmt`
    /// and provides the necessary functionality to handle this specific type in
    /// the SQL engine, supporting both precision keywords (e.g., "NANOSECOND")
    /// and numeric modifiers (e.g., `TIMESTAMP(6)`).
    ///
    /// Example SQL Usage:
    ///   CREATE TABLE events (
    ///       event_time TIMESTAMP,  -- Standard TIMESTAMP type
    ///       event_time_nano TIMESTAMP NANOSECOND,  -- TIMESTAMP with nanosecond precision
    ///       event_time_milli TIMESTAMP MILLISECOND,  -- TIMESTAMP with millisecond precision
    ///       event_time_custom TIMESTAMP(6)  -- TIMESTAMP with 6 digits of precision (microseconds)
    ///   );
    ///
    /// The precision keyword indicates the level of precision, where `NANOSECOND`
    /// is the highest precision (nanoseconds). Alternatively, numeric modifiers
    /// like `TIMESTAMP(6)` allow specifying the precision numerically.
    ////////////////////////////////////////////////////////////////////////////////

    struct TypeNameTimestamp final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "TIMESTAMP";

        TypeNameTimestamp() : TypeStmt(pb::ast::TYPE_NAME_TIMESTAMP) {
        }

        static TypeNameTimestamp *make_typename(turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameTimestamp>(arena);
            String name;
            name = TYPE_NAME;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;

        pb::ast::TimeStampPrecision precision{pb::ast::TimeStampPrecision::TS_NANO};
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// TypeNameDuration - Represents the `DURATION` type in SQL with precision.
    ///
    /// This class models the `DURATION` type, which represents a length of time
    /// (e.g., "5 hours", "10 minutes", etc.), including its precision (e.g.,
    /// seconds, milliseconds, microseconds, nanoseconds). It extends `TypeStmt`
    /// and provides the necessary functionality to handle this specific type in
    /// the SQL engine, supporting both precision keywords (e.g., "NANOSECOND")
    /// and numeric modifiers (e.g., `DURATION(6)`).
    ///
    /// Example SQL Usage:
    ///   CREATE TABLE events (
    ///       event_duration DURATION,  -- Standard DURATION type
    ///       event_duration_nano DURATION NANOSECOND,  -- DURATION with nanosecond precision
    ///       event_duration_milli DURATION MILLISECOND,  -- DURATION with millisecond precision
    ///       event_duration_custom DURATION(6)  -- DURATION with 6 digits of precision (microseconds)
    ///   );
    ///
    /// The precision keyword indicates the level of precision, where `NANOSECOND`
    /// is the highest precision (nanoseconds). Alternatively, numeric modifiers
    /// like `DURATION(6)` allow specifying the precision numerically.
    ////////////////////////////////////////////////////////////////////////////////

    struct TypeNameDuration final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "DURATION";

        TypeNameDuration() : TypeStmt(pb::ast::TYPE_NAME_DURATION) {
        }

        static TypeNameDuration *make_typename(turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameDuration>(arena);
            String name;
            name = TYPE_NAME;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;

        pb::ast::TimeStampPrecision precision{pb::ast::TimeStampPrecision::TS_NANO};
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// TypeNameDateTime - Represents the `DATETIME` type in SQL with precision and time zone.
    ///
    /// This class models the `DATETIME` type, which represents both a date and
    /// a time. It includes precision (e.g., seconds, milliseconds, microseconds,
    /// nanoseconds) and time zone support (e.g., `DATETIME('tz=UTC')`). This is in contrast
    /// to the traditional `WITH TIME ZONE` syntax, where the time zone is specified
    /// through a modifiable string parameter (`tz=xxx`). The class extends `TypeStmt`
    /// and provides the necessary functionality to handle this specific type in the SQL engine.
    ///
    /// Supported SQL Usage:
    ///
    ///   1. **Standard `DATETIME`**:
    ///      `DATETIME` by default uses second-level precision, which means it stores
    ///      the date and time down to the second (without fractions of a second).
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE events (
    ///          event_time DATETIME  -- Default precision (second-level)
    ///      );
    ///      ```
    ///      This will create a column `event_time` that stores date-time values
    ///      to the second precision, with no fractional part.
    ///
    ///   2. **Precision Modification (`DATETIME(n)`)**:
    ///      The precision of `DATETIME` can be further refined by adding a numeric modifier,
    ///      such as `DATETIME(6)` for microsecond precision. The number in parentheses
    ///      specifies the fractional precision (e.g., `DATETIME(6)` for microseconds,
    ///      `DATETIME(3)` for milliseconds).
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE events (
    ///          event_time DATETIME(6)  -- Microsecond precision
    ///      );
    ///      ```
    ///      This will create a column `event_time` that stores date-time values
    ///      to microsecond precision (6 digits after the decimal point).
    ///
    ///   3. **Precision Keywords (`DATETIME SECOND`, `DATETIME NANOSECOND`, etc.)**:
    ///      In addition to numeric precision modifiers, `DATETIME` types can use specific keywords
    ///      like `SECOND`, `MILLISECOND`, `MICROSECOND`, and `NANOSECOND` to denote the level
    ///      of precision. These are generally used in place of numeric precision modifiers.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE events (
    ///          event_time DATETIME NANOSECOND  -- Nanosecond precision
    ///      );
    ///      ```
    ///      This creates a column `event_time` with nanosecond precision, storing date-time values
    ///      to the finest level of precision.
    ///
    ///   4. **Time Zone Modification (`DATETIME('tz=xxx')`)**:
    ///      Instead of using `WITH TIME ZONE`, time zones are now specified as a string parameter
    ///      using the `DATETIME('tz=xxx')` syntax. The `xxx` represents a valid time zone identifier
    ///      (e.g., `UTC`, `PST`, `GMT`), allowing you to define the time zone for storing date-time
    ///      values.
    ///
    ///      Examples:
    ///      - `DATETIME('tz=UTC')`: Represents `DATETIME` with UTC time zone.
    ///      - `DATETIME('tz=PST')`: Represents `DATETIME` with Pacific Standard Time (PST) time zone.
    ///
    ///      Example SQL:
    ///      ```sql
    ///      CREATE TABLE events (
    ///          event_time_with_tz DATETIME('tz=UTC')  -- Time zone specified as UTC
    ///      );
    ///      ```
    ///      This will create a column `event_time_with_tz` where the date-time values are stored
    ///      using the UTC time zone.
    ///
    ///      Another example:
    ///      ```sql
    ///      CREATE TABLE events (
    ///          event_time_with_tz DATETIME('tz=PST')  -- Time zone specified as PST
    ///      );
    ///      ```
    ///      This will create a column `event_time_with_tz` where the date-time values are stored
    ///      using the Pacific Standard Time (PST) time zone.
    ///
    ///   5. **Combined Precision and Time Zone (`DATETIME(6, 'tz=UTC')`)**:
    ///      You can combine both precision and time zone specifications within a single `DATETIME`
    ///      type. For instance, `DATETIME(6, 'tz=UTC')` specifies microsecond precision along with
    ///      the UTC time zone.
    ///
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE events (
    ///          event_time DATETIME(6, 'tz=UTC')  -- Microsecond precision with UTC time zone
    ///      );
    ///      ```
    ///      This creates a column `event_time` that stores date-time values with microsecond precision
    ///      and UTC time zone.
    ////////////////////////////////////////////////////////////////////////////////

    struct TypeNameDateTime final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "DATETIME";

        TypeNameDateTime() : TypeStmt(pb::ast::TYPE_NAME_DATETIME) {
        }

        static TypeNameDateTime *make_typename(turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameDateTime>(arena);
            String name;
            name = TYPE_NAME;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;

        pb::ast::TimeStampPrecision precision{pb::ast::TimeStampPrecision::TS_NANO};
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// TypeNameInterval - Represents the `INTERVAL` type in SQL.
    ///
    /// This class models the `INTERVAL` type, which represents a span of time
    /// between two points. The `INTERVAL` type can have a precision (e.g.,
    /// seconds, milliseconds, microseconds, nanoseconds) and can be used in
    /// expressions to represent durations in SQL. The class extends `TypeStmt`
    /// and provides the necessary functionality to handle this specific type in
    /// the SQL engine, supporting various interval units and precision levels.
    ///
    /// Supported SQL Usage:
    ///
    ///   1. **Basic `INTERVAL` Type**:
    ///      The `INTERVAL` type is used to represent a time duration.
    ///      The default behavior is to store the interval without any specific
    ///      precision, usually treating it as a generic interval, often assumed
    ///      to be in seconds unless otherwise specified.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE events (
    ///          event_duration INTERVAL  -- Default interval (no specific precision)
    ///      );
    ///      ```
    ///      This creates an `event_duration` column that represents time intervals.
    ///      The interval may default to seconds or a general duration, depending on the SQL engine.
    ///
    ///   2. **Interval with Unit (`INTERVAL SECOND`)**:
    ///      The `INTERVAL` type can use `SECOND` to specify time intervals in seconds.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE events (
    ///          event_duration INTERVAL SECOND  -- Interval with second precision
    ///      );
    ///      ```
    ///      This creates an `event_duration` column where the time interval is measured in
    ///      seconds, with no fractional part.
    ///
    ///   3. **Interval with Precision (`INTERVAL SECOND`)**:
    ///      The precision for the `INTERVAL SECOND` type is defined by the SQL engine,
    ///      and the fractional precision is automatically handled. For example, precision
    ///      for seconds can be internally stored in microseconds, nanoseconds, or other units,
    ///      but this does not require explicit mention in the SQL syntax.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE events (
    ///          event_duration INTERVAL SECOND  -- Interval in seconds (precision determined by engine)
    ///      );
    ///      ```
    ///      This creates an `event_duration` column with time intervals stored in seconds
    ///      (precision handled by the engine).
    ///
    ///   4. **Interval with Time Units (`INTERVAL SECOND`, `INTERVAL MINUTE`, etc.)**:
    ///      The `INTERVAL` type can also use other units to specify the time interval, such as:
    ///      - `SECOND`, `MINUTE`, `HOUR`, `DAY`, etc.
    ///      Example with minute precision:
    ///      ```sql
    ///      CREATE TABLE events (
    ///          event_duration INTERVAL MINUTE  -- Interval in minutes
    ///      );
    ///      ```
    ///      This creates an `event_duration` column with time intervals expressed in minutes.
    ///
    ///      Example with hour precision:
    ///      ```sql
    ///      CREATE TABLE events (
    ///          event_duration INTERVAL HOUR  -- Interval in hours
    ///      );
    ///      ```
    ///      This creates an `event_duration` column with time intervals expressed in hours.
    ////////////////////////////////////////////////////////////////////////////////
    struct TypeNameInterval final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "INTERVAL";

        TypeNameInterval() : TypeStmt(pb::ast::TYPE_NAME_INTERVAL) {
        }

        static TypeNameInterval *make_typename(turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameInterval>(arena);
            String name;
            name = TYPE_NAME;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;

        pb::ast::TimeStampPrecision precision{pb::ast::TimeStampPrecision::TS_NANO};
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// TypeNameJson - Represents the `JSON` type in SQL.
    ///
    /// This class models the `JSON` type, which is used to store JSON-formatted data.
    /// The `JSON` type allows for storing complex data structures like arrays and objects
    /// as a single value, providing the ability to query and manipulate them efficiently.
    /// The class extends `TypeStmt` and provides the necessary functionality to handle
    /// this specific type in the SQL engine, including storage and type validation for JSON data.
    ///
    /// Supported SQL Usage:
    ///
    ///   1. **Basic `JSON` Type**:
    ///      The `JSON` type is used to represent arbitrary JSON data. It can store objects,
    ///      arrays, and primitive values such as strings, numbers, booleans, and `null`.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE products (
    ///          id INT PRIMARY KEY,
    ///          product_info JSON  -- A column to store arbitrary JSON data
    ///      );
    ///      ```
    ///      This creates a `product_info` column, which can store any valid JSON object,
    ///      array, or primitive value. Data can be inserted as JSON literals or parsed into
    ///      JSON format using SQL functions like `JSON_OBJECT` or `JSON_ARRAY`.
    ///
    ///   2. **Inserting JSON Data**:
    ///      You can insert JSON data into the `JSON` column by using a JSON literal or
    ///      converting other types to JSON format.
    ///      Example:
    ///      ```sql
    ///      INSERT INTO products (id, product_info)
    ///      VALUES (1, '{"name": "Laptop", "price": 1299.99, "available": true}');
    ///      ```
    ///      This inserts a JSON object representing a product with properties such as `name`, `price`, and `available`.
    ///
    ///   3. **Querying JSON Data**:
    ///      SQL provides functions to query and manipulate JSON data. For example, you can use
    ///      functions like `JSON_EXTRACT`, `JSON_UNQUOTE`, and `JSON_ARRAY_LENGTH` to interact with the
    ///      stored JSON data.
    ///      Example:
    ///      ```sql
    ///      SELECT JSON_EXTRACT(product_info, '$.name') AS product_name
    ///      FROM products
    ///      WHERE JSON_EXTRACT(product_info, '$.price') > 1000;
    ///      ```
    ///      This query extracts the `name` property from the `product_info` JSON column and filters
    ///      rows where the `price` is greater than 1000.
    ///
    ///   4. **JSON Data Validation**:
    ///      The `JSON` type also supports validation of the JSON format, ensuring that data inserted
    ///      into a `JSON` column is always valid JSON. If invalid JSON is inserted, the database
    ///      engine will return an error, preventing corrupted data from being stored.
    ///      Example:
    ///      ```sql
    ///      INSERT INTO products (id, product_info)
    ///      VALUES (2, '{"name": "Smartphone", "price": "invalid"}');  -- Error: Invalid JSON format
    ///      ```
    ///      This will fail because the `price` field is a string instead of a valid number.
    ////////////////////////////////////////////////////////////////////////////////

    struct TypeNameJson final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "JSON";

        TypeNameJson() : TypeStmt(pb::ast::TYPE_NAME_JSON) {
        }

        static TypeNameJson *make_typename(turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameJson>(arena);
            String name;
            name = TYPE_NAME;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// TypeNameBitmap - Represents the `BITMAP` type in SQL.
    ///
    /// This class models the `BITMAP` type, which is commonly used for efficient
    /// storage and manipulation of large sets of bitmap data. The `BITMAP` type
    /// can be used for tasks such as set operations, data compression,
    /// and efficient aggregation. It can also be used for features like
    /// indexing, bitwise operations, and filtering large datasets in an efficient
    /// manner. However, support for actual execution and runtime operations
    /// on the `BITMAP` type is not yet implemented in this version.
    ///
    /// SQL Usage:
    ///   The `BITMAP` type allows for the representation of a bitmap index or a
    ///   large set of binary flags stored as a bitmap (bitset).
    ///   Example:
    ///   ```sql
    ///   CREATE TABLE users (
    ///       id INT PRIMARY KEY,
    ///       interests BITMAP  -- A column to store bitmap data representing user interests
    ///   );
    ///   ```
    ///   This creates a `users` table with a `BITMAP` column named `interests`,
    ///   which will hold bitmap data to represent user interests as flags or sets.
    ///
    ///   **Note:**
    ///      - The current implementation supports the syntax for `BITMAP` types,
    ///        but runtime execution of operations like set operations, bitwise
    ///        operations, or aggregation on `BITMAP` columns is not yet supported.
    ///      - Future implementations may add the required backend support for
    ///        processing bitmap data and performing bitmap-related operations.
    ///
    ///   Current Limitation:
    ///      - Although the SQL syntax for the `BITMAP` type is available, actual
    ///        support for running queries or performing bitmap operations (e.g.,
    ///        intersections, unions) is not yet available in this version of the engine.
    ///      - The `BITMAP` type may be used in table schemas, but querying or modifying
    ///        `BITMAP` columns will not be functional until the appropriate engine
    ///        enhancements are added to handle bitmap data processing efficiently.
    ///
    ///   Future Work:
    ///      - Bitmap-specific operations like `BITMAP_UNION`, `BITMAP_INTERSECT`,
    ///        and `BITMAP_COUNT` might be added in future versions of the database.
    ///      - Bitmap compression techniques and optimizations to improve storage and
    ///        querying performance will also be explored.
    ////////////////////////////////////////////////////////////////////////////////

    struct TypeNameBitmap final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "BITMAP";

        TypeNameBitmap() : TypeStmt(pb::ast::TYPE_NAME_BITMAP) {
        }

        static TypeNameBitmap *make_typename(turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameBitmap>(arena);
            String name;
            name = TYPE_NAME;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;
    };

    struct TypeNameTdigest final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "TDIGEST";

        TypeNameTdigest() : TypeStmt(pb::ast::TYPE_NAME_TDIGEST) {
        }

        static TypeNameTdigest *make_typename(turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameTdigest>(arena);
            String name;
            name = TYPE_NAME;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;
    };

    struct TypeNameHll final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "HLL";

        TypeNameHll() : TypeStmt(pb::ast::TYPE_NAME_HLL) {
        }

        static TypeNameHll *make_typename(turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameHll>(arena);
            String name;
            name = TYPE_NAME;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// TypeNameString - Represents SQL string types (`STRING` and `BINARY`).
    ///
    /// This class models the `STRING` and `BINARY` types in SQL, which are used to store
    /// variable-length character data. Both `STRING` and `BINARY` are represented
    /// by this class, with the distinction between the two being defined by the `is_binary` flag.
    ///
    /// Supported SQL Usage:
    ///
    ///   1. **`STRING` Type**:
    ///      The `STRING` type is used to store variable-length character data.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE users (
    ///          id INT PRIMARY KEY,
    ///          name STRING  -- A column to store variable-length string data
    ///      );
    ///      ```
    ///      This creates a `name` column that stores any variable-length text or character
    ///      data, with no specific encoding or binary representation.
    ///
    ///   2. **`BINARY` Type**:
    ///      The `BINARY` type is used to store variable-length binary data. It is similar to `STRING`,
    ///      but the data is treated as raw bytes instead of encoded text. This is often used for storing
    ///      files, images, or other binary blobs.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE files (
    ///          id INT PRIMARY KEY,
    ///          data BINARY  -- A column to store raw binary data
    ///      );
    ///      ```
    ///      This creates a `data` column that stores binary data, such as files or images.
    ///
    ///   3. **Character Set and Collation**:
    ///      Both `STRING` and `BINARY` types can have an associated character set (`charset`) and collation
    ///      (`collate`). These are used to specify the encoding and sorting rules for the column's values.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE users (
    ///          id INT PRIMARY KEY,
    ///          name STRING(UTF8) COLLATE utf8_general_ci  -- Column with a specific charset and collation
    ///      );
    ///      ```
    ///      This creates a `name` column that stores string data encoded using UTF-8 and sorted according to
    ///      the `utf8_general_ci` collation rules.
    ///
    ///   4. **Modifying String Types**:
    ///      The `STRING` type can also be modified by specifying the character set or collation,
    ///      or by using the `BINARY` keyword to store raw binary data instead of text.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE documents (
    ///          id INT PRIMARY KEY,
    ///          content STRING COLLATE utf8_bin  -- String with specific collation
    ///      );
    ///      ```
    ///      This example creates a `content` column where the strings are sorted using the `utf8_bin` collation.
    ///
    ///      ```sql
    ///      CREATE TABLE files (
    ///          id INT PRIMARY KEY,
    ///          data BINARY  -- Binary data storage
    ///      );
    ///      ```
    ///      This creates a `data` column that stores binary data without any character encoding.
    ////////////////////////////////////////////////////////////////////////////////

    struct TypeNameString final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "string";
        static constexpr const char *TYPE_STRING = "STRING";
        static constexpr const char *TYPE_BINARY = "BINARY";

        TypeNameString() : TypeStmt(pb::ast::TYPE_NAME_STRING) {
            charset = nullptr;
            collate = nullptr;
        }

        static TypeNameString *make_typename(bool binary, turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameString>(arena);
            String name;
            ptr->is_binary = binary;
            name = binary ? TYPE_BINARY : TYPE_STRING;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;

        String charset;
        String collate;
        bool is_binary = false;
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// TypeNameMap - Represents the `MAP` type in SQL.
    ///
    /// This class models the `MAP` type, which is used to store key-value pairs.
    /// The `MAP` type is a collection of entries where each entry consists of a key
    /// and a corresponding value. Both the key and value types are defined by
    /// `key` and `value` members respectively, allowing flexibility in defining
    /// the types of data that can be stored in the map. In SQL, `MAP` can be used
    /// for scenarios where associating a value with a specific key is necessary,
    /// such as storing configurations, settings, or associative arrays.
    ///
    /// SQL Usage:
    ///   1. **Basic `MAP` Type**:
    ///      The `MAP` type represents an associative array or dictionary. It is composed of
    ///      a set of key-value pairs, where the key is mapped to a corresponding value.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE configurations (
    ///          id INT PRIMARY KEY,
    ///          settings MAP(STRING, STRING)  -- A MAP column that maps keys (STRING) to values (STRING)
    ///      );
    ///      ```
    ///      This creates a `settings` column that stores a `MAP` where both keys and values are `STRING` types.
    ///      You can store configuration data as key-value pairs, for example, mapping configuration names to values.
    ///
    ///   2. **Complex Types in MAP**:
    ///      You can also define more complex types for the keys or values in the `MAP`. For example,
    ///      the key could be an `INT`, and the value could be a `JSON` or a `STRUCT`.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE users (
    ///          id INT PRIMARY KEY,
    ///          preferences MAP(INT, JSON)  -- A MAP where keys are INT and values are JSON
    ///      );
    ///      ```
    ///      This creates a `preferences` column where the key is an integer (e.g., a preference ID)
    ///      and the value is a `JSON` object (e.g., the actual preference data).
    ///
    ///   3. **Modifying MAP Type**:
    ///      You can define a `MAP` with specific key-value pairs, as long as the key and value types
    ///      are valid. For example, a `MAP` with `STRING` keys and `INT` values can be used to store
    ///      mappings between names and ages.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE products (
    ///          id INT PRIMARY KEY,
    ///          inventory MAP(STRING, INT)  -- A MAP that associates product names with quantities
    ///      );
    ///      ```
    ///      This stores product names as the keys and their quantities as the values.
    ///
    ///   4. **Queries on MAP Type**:
    ///      Queries can be made on the `MAP` type to extract specific values based on the key.
    ///      Example:
    ///      ```sql
    ///      SELECT settings['theme'] FROM configurations WHERE id = 1;
    ///      ```
    ///      This query retrieves the value of the `theme` setting from the `settings` map in the `configurations` table.
    ///
    ///      ```sql
    ///      SELECT preferences[1] FROM users WHERE id = 2;
    ///      ```
    ///      This query retrieves the `JSON` value for the preference with ID `1` from the `preferences`
    ///      map in the `users` table.
    ///
    ///   5. **Key and Value Type Validation**:
    ///      SQL engines typically validate the types of the keys and values in a `MAP`. The key type must
    ///      be a scalar type (such as `STRING`, `INT`, etc.), and the value type can be any valid type,
    ///      including `STRUCT`, `JSON`, or even another `MAP` (for nested maps).
    ///
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE settings (
    ///          id INT PRIMARY KEY,
    ///          config MAP(STRING, STRUCT(name STRING, value STRING))  -- A nested STRUCT as the value type
    ///      );
    ///      ```
    ///      This creates a `config` column that stores a `MAP` where the key is a `STRING` and the value is
    ///      a `STRUCT` with two `STRING` fields: `name` and `value`.
    ///
    ///      **Note:**
    ///      - While this implementation allows for the modeling of the `MAP` type, runtime operations such as
    ///      `INSERT`, `UPDATE`, and `SELECT` on `MAP` columns are expected to be supported in future database engine releases.
    ///      - This class provides the basic structure, but the actual support for key-value operations (e.g., lookups,
    ///         updates) will be added as part of the engine's functionality.
    ////////////////////////////////////////////////////////////////////////////////

    struct TypeNameMap final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "MAP";

        TypeNameMap() : TypeStmt(pb::ast::TYPE_NAME_MAP) {
        }

        static TypeNameMap *make_typename(turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameMap>(arena);
            String name;
            name = TYPE_NAME;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;

        TypeStmt *key{nullptr};
        TypeStmt *value{nullptr};
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// TypeNameSet - Represents the `SET` type in SQL.
    ///
    /// This class models the `SET` type in SQL, which is used to store a collection
    /// of unique values. The `SET` type is similar to a mathematical set, where each
    /// element is unique, and the order of elements does not matter. The values in the
    /// set can be of any valid type (e.g., `STRING`, `INT`, etc.). The key member is used
    /// to define the type of elements stored in the `SET`.
    ///
    /// SQL Usage:
    ///   1. **Basic `SET` Type**:
    ///      The `SET` type is used to store a collection of unique elements. It does not allow
    ///      duplicate values and does not define an order for the elements.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE users (
    ///          id INT PRIMARY KEY,
    ///          tags SET(STRING)  -- A column to store a set of tags (unique strings)
    ///      );
    ///      ```
    ///      This creates a `tags` column that stores a `SET` of `STRING` elements. Each value
    ///      in the set must be unique, and the order does not matter. You can store tags like
    ///      "sports", "music", "travel", etc., but each tag will only appear once.
    ///
    ///   2. **Complex Types in SET**:
    ///      You can also define more complex types for the elements in the `SET`. For example,
    ///      the set could store integers, JSON data, or even `STRUCT` types.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE orders (
    ///          id INT PRIMARY KEY,
    ///          status SET(STRING)  -- A set of statuses (e.g., 'pending', 'shipped', 'delivered')
    ///      );
    ///      ```
    ///      This stores a set of statuses that the order can have.
    ///
    ///      ```sql
    ///      CREATE TABLE preferences (
    ///          id INT PRIMARY KEY,
    ///          options SET(INT)  -- A set of integer options
    ///      );
    ///      ```
    ///      This stores a set of integers representing selected options.
    ///
    ///   3. **Querying the `SET` Type**:
    ///      SQL engines typically provide operations to interact with `SET` types. You can check
    ///      if a specific element is part of the set or query for the set's elements.
    ///      Example:
    ///      ```sql
    ///      SELECT tags FROM users WHERE id = 1 AND 'sports' IN tags;
    ///      ```
    ///      This query returns the `tags` set for a user where the `tags` set includes "sports".
    ///
    ///      ```sql
    ///      SELECT options FROM preferences WHERE id = 2 AND 1 IN options;
    ///      ```
    ///      This query checks whether the `options` set for a particular preference includes the value `1`.
    ///
    ///   4. **Modifying `SET` Type**:
    ///      You can add or remove elements from a `SET` in SQL. For example, you can add a new tag
    ///      to a user's `tags` set or remove an option from a user's `options` set.
    ///      Example:
    ///      ```sql
    ///      UPDATE users SET tags = tags + 'coding' WHERE id = 1;
    ///      ```
    ///      This adds "coding" to the `tags` set for the user with ID 1.
    ///
    ///      ```sql
    ///      UPDATE preferences SET options = options - 2 WHERE id = 2;
    ///      ```
    ///      This removes the option `2` from the `options` set for the preference with ID 2.
    ///
    ///   **Note:**
    ///      - The `SET` type is useful when you need to represent collections of unique items,
    ///        such as tags, options, or categories, and you do not care about the order of elements.
    ///      - This class provides the basic structure to define and store the `SET` type. Actual
    ///        support for operations like adding/removing elements or querying the set will depend
    ///        on the database engine.
    ////////////////////////////////////////////////////////////////////////////////

    struct TypeNameSet final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "SET";

        TypeNameSet() : TypeStmt(pb::ast::TYPE_NAME_SET) {
        }

        static TypeNameSet *make_typename(turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameSet>(arena);
            String name;
            name = TYPE_NAME;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;

        TypeStmt *key{nullptr};
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// TypeNameTuple - Represents the `TUPLE` type in SQL.
    ///
    /// This class models the `TUPLE` type in SQL, which is used to store a collection
    /// of elements of different types, similar to a struct or a record in programming languages.
    /// A `TUPLE` type allows you to store a set of related values, each potentially having
    /// a different data type, in a single column. The `TUPLE` type is commonly used to
    /// represent complex objects or composite types where multiple pieces of data are grouped
    /// together into one entity.
    ///
    /// SQL Usage:
    ///   1. **Basic `TUPLE` Type**:
    ///      The `TUPLE` type is used to represent a collection of multiple fields of different
    ///      types. Each field in the `TUPLE` is defined by its own type. It is similar to a
    ///      `STRUCT` type in many SQL dialects, but the key difference is that it is not necessarily
    ///      named or indexed. It is an ordered collection of elements.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE employees (
    ///          id INT PRIMARY KEY,
    ///          contact TUPLE(STRING, STRING, INT)  -- A tuple of (name, email, age)
    ///      );
    ///      ```
    ///      This creates a `contact` column that stores a tuple containing three elements:
    ///      a `STRING` for name, a `STRING` for email, and an `INT` for age.
    ///
    ///   2. **Querying `TUPLE` Type**:
    ///      You can query the individual fields of a `TUPLE` by referencing the position of
    ///      the fields in the tuple. Typically, the fields in the tuple are accessed in a
    ///      specific order, and you would use array-like indexing or direct references to
    ///      query the elements.
    ///      Example:
    ///      ```sql
    ///      SELECT contact[1] AS name, contact[2] AS email FROM employees;
    ///      ```
    ///      This query retrieves the first and second elements of the `contact` tuple (i.e.,
    ///      `name` and `email`) from the `employees` table.
    ///
    ///      ```sql
    ///      SELECT contact[3] FROM employees WHERE id = 1;
    ///      ```
    ///      This query retrieves the third element (age) of the `contact` tuple for the employee
    ///      with `id = 1`.
    ///
    ///   3. **Complex `TUPLE` Types**:
    ///      You can create `TUPLE` types that contain other complex types, such as `TUPLE`
    ///      or `STRUCT` types, enabling nested data structures.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE customers (
    ///          id INT PRIMARY KEY,
    ///          address TUPLE(STRING, TUPLE(STRING, STRING))  -- Address is a tuple with a nested tuple
    ///      );
    ///      ```
    ///      In this example, `address` is a tuple that contains two elements: a `STRING` for street
    ///      and a nested `TUPLE` containing `STRING` values for city and zip code.
    ///
    ///   4. **Modifying `TUPLE` Values**:
    ///      SQL engines generally allow for modifying specific elements of a `TUPLE` using
    ///      positional references. For example, you might want to update a specific field in
    ///      the tuple, like changing the `age` field for a particular employee.
    ///      Example:
    ///      ```sql
    ///      UPDATE employees SET contact[3] = 30 WHERE id = 1;
    ///      ```
    ///      This updates the `age` field (the third element) of the `contact` tuple for the employee with `id = 1`.
    ///
    ///   **Note:**
    ///      - The `TUPLE` type is useful when you need to represent a collection of related
    ///        values that have different data types, such as composite keys, addresses, or
    ///        customer profiles.
    ///      - `TUPLE` types in SQL can be used for grouping different data together, enabling
    ///        more complex, nested data structures in a single column.
    ///      - While this class defines the structure of a `TUPLE` type, the actual functionality
    ///        of querying, updating, and interacting with tuples in SQL depends on the database
    ///        engine's implementation.
    ////////////////////////////////////////////////////////////////////////////////

    struct TypeNameTuple final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "TUPLE";

        TypeNameTuple() : TypeStmt(pb::ast::TYPE_NAME_TUPLE) {
        }

        static TypeNameTuple *make_typename(turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameTuple>(arena);
            String name;
            name = TYPE_NAME;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;

        Vector<TypeStmt *> type_args;
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// TypeNameUnion - Represents the `UNION` type in SQL.
    ///
    /// This class models the `UNION` type in SQL, which is used to represent a
    /// collection of elements that can be of different types. Similar to the
    /// `TUPLE` type, the `UNION` type allows you to store multiple types in a
    /// single column, but unlike `TUPLE`, it does not require a fixed number
    /// of fields or elements. The `UNION` type can be used to represent a field
    /// that can hold multiple different types, but only one of them will be stored
    /// at any given time, similar to the concept of a `union` in programming languages.
    ///
    /// SQL Usage:
    ///   1. **Basic `UNION` Type**:
    ///      The `UNION` type is used to represent a field that can store one of several
    ///      possible types. Each type in the union is defined, and only one type can be
    ///      stored in the union at a time. This type is useful when you have a column
    ///      that can hold multiple different data types, but each row will only hold
    ///      one of those types.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE mixed_data (
    ///          id INT PRIMARY KEY,
    ///          data UNION(STRING, INT, DOUBLE)  -- A union column that can store STRING, INT, or DOUBLE
    ///      );
    ///      ```
    ///      This creates a `data` column that can store either a `STRING`, `INT`, or `DOUBLE`.
    ///      For each row, the `data` column will hold only one of these types at any given time.
    ///
    ///   2. **Querying the `UNION` Type**:
    ///      To interact with a `UNION` type, you would typically check the type of the data
    ///      stored in the column and use appropriate casting or type checks to interpret the value.
    ///      Example:
    ///      ```sql
    ///      SELECT data FROM mixed_data WHERE id = 1;
    ///      ```
    ///      Depending on how the data is stored (as a `STRING`, `INT`, or `DOUBLE`), you
    ///      may need to handle different types in your application logic.
    ///
    ///   3. **Union with Multiple Types**:
    ///      The `UNION` type can also store more complex types. For example, you can store
    ///      either an integer, a string, or even a complex structure (like a `TUPLE` or `STRUCT`).
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE event_log (
    ///          id INT PRIMARY KEY,
    ///          event_data UNION(STRING, TUPLE(STRING, INT))  -- A union of STRING or a TUPLE
    ///      );
    ///      ```
    ///      Here, the `event_data` column can store either a `STRING` or a `TUPLE` of `STRING` and `INT`.
    ///
    ///   4. **Modifying `UNION` Values**:
    ///      SQL engines typically allow modifying the data stored in a `UNION` type. Since
    ///      only one of the types in the `UNION` can be stored at a time, an update operation
    ///      will modify the value to one of the other types defined in the `UNION`.
    ///      Example:
    ///      ```sql
    ///      UPDATE mixed_data SET data = 123 WHERE id = 1;
    ///      ```
    ///      This would store an `INT` value of `123` in the `data` column of the row with `id = 1`.
    ///
    ///   **Note:**
    ///      - The `UNION` type is useful when you need to represent a column that can hold
    ///        one of several different types, where only one of them will be used at any
    ///        given time. It's similar to a `TUPLE`, but with multiple independent possibilities.
    ///      - This class defines the `UNION` type, which can store different types (like `STRING`,
    ///        `INT`, `TUPLE`, etc.), and the specific type stored in each row will depend on
    ///        the data for that row.
    ///      - The SQL engine or application logic should handle the data type of the value
    ///        stored in the `UNION` column to ensure that the correct type is used and queried
    ///        appropriately.
    ////////////////////////////////////////////////////////////////////////////////

    struct TypeNameUnion final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "UNION";

        TypeNameUnion() : TypeStmt(pb::ast::TYPE_NAME_UNION) {
        }

        static TypeNameUnion *make_typename(turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameUnion>(arena);
            String name;
            name = TYPE_NAME;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;

        Vector<NamedType *> type_args;
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// TypeNameStruct - Represents the `STRUCT` type in SQL.
    ///
    /// This class models the `STRUCT` type in SQL, which is used to represent a
    /// composite type consisting of multiple named fields. Each field in the `STRUCT`
    /// type can have a different data type, and each field is accessed by its name.
    /// This type is similar to `TUPLE`, but the key difference is that the fields
    /// in a `STRUCT` are named and more akin to a record or object in programming languages.
    ///
    /// SQL Usage:
    ///   1. **Basic `STRUCT` Type**:
    ///      The `STRUCT` type is used to represent a collection of fields that are related
    ///      and have different types. Each field in the `STRUCT` has a name and a type,
    ///      making it easier to query and reference individual fields by their names.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE employees (
    ///          id INT PRIMARY KEY,
    ///          address STRUCT(street STRING, city STRING, zip INT)  -- A struct with fields for address
    ///      );
    ///      ```
    ///      This creates an `address` column that contains a `STRUCT` with three fields:
    ///      - `street` (STRING)
    ///      - `city` (STRING)
    ///      - `zip` (INT)
    ///
    ///   2. **Querying `STRUCT` Fields**:
    ///      You can query individual fields of a `STRUCT` by referencing their names.
    ///      Example:
    ///      ```sql
    ///      SELECT address.street AS street, address.city AS city FROM employees;
    ///      ```
    ///      This query retrieves the `street` and `city` fields from the `address` column of the `employees` table.
    ///
    ///      To retrieve a nested field within a `STRUCT` (if the `STRUCT` has nested `STRUCT` fields),
    ///      you can use dot notation to access the nested fields:
    ///      ```sql
    ///      SELECT address.zip FROM employees;
    ///      ```
    ///      This retrieves the `zip` field from the `address` column.
    ///
    ///   3. **Nested `STRUCT` Types**:
    ///      A `STRUCT` can contain other `STRUCT` types, allowing for deeply nested data structures.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE companies (
    ///          id INT PRIMARY KEY,
    ///          ceo STRUCT(name STRING, age INT, address STRUCT(street STRING, city STRING))
    ///      );
    ///      ```
    ///      Here, the `ceo` field is itself a `STRUCT` containing another `STRUCT` for the address.
    ///
    ///   4. **Modifying `STRUCT` Values**:
    ///      SQL engines typically allow for modifying specific fields of a `STRUCT`.
    ///      You can update individual fields by referencing their names.
    ///      Example:
    ///      ```sql
    ///      UPDATE employees SET address.city = 'New York' WHERE id = 1;
    ///      ```
    ///      This query updates the `city` field of the `address` `STRUCT` for the employee with `id = 1`.
    ///
    ///   **Note:**
    ///      - The `STRUCT` type is useful when you need to represent a collection of related data
    ///        that may have different types. It provides a way to model complex records, objects,
    ///        or nested data structures.
    ///      - Unlike `TUPLE`, the fields of a `STRUCT` are named, making it easier to refer to and
    ///        query specific elements of the `STRUCT`.
    ///      - `STRUCT` types in SQL can be nested, allowing you to create hierarchical data models
    ///        within a single column.
    ///      - When querying or updating `STRUCT` types, you need to specify the field names to
    ///        access or modify specific elements.
    ////////////////////////////////////////////////////////////////////////////////

    struct TypeNameStruct final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "STRUCT";

        TypeNameStruct() : TypeStmt(pb::ast::TYPE_NAME_STRUCT) {
        }

        static TypeNameStruct *make_typename(turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameStruct>(arena);
            String name;
            name = TYPE_NAME;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;

        Vector<NamedType *> type_args;
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// TypeNameProto - Represents the `PROTO` type in SQL.
    ///
    /// This class models the `PROTO` type in SQL, which is used to store serialized
    /// Protocol Buffers (Protobuf) data. Protobuf is a binary serialization format
    /// developed by Google, used for efficient and compact data storage and transmission.
    /// In a SQL database, the `PROTO` type is used to store structured data
    /// in a serialized binary format defined by a Protobuf schema (via a `.proto` file).
    ///
    /// **Current Limitations:**
    ///   - The `PROTO` type in the current implementation **only supports storing the URI**
    ///     (location of the Protobuf schema) and the **binary Protobuf data**.
    ///   - Dynamic extraction of fields or querying the contents of the Protobuf data directly in SQL
    ///     is **not supported** at the database level.
    ///   - To access or manipulate the fields within the Protobuf data, **third-party libraries**
    ///     (such as Google's Protobuf library) must be used at the **application level**.
    ///   - This approach is more flexible but also introduces **potential risks** (e.g.,
    ///     incorrect schema handling, security concerns) if not properly managed.
    ///
    /// SQL Usage:
    ///   1. **Basic `PROTO` Type**:
    ///      The `PROTO` type is used to store data in a binary Protobuf format. The actual
    ///      structure of the data is defined by a Protobuf schema file (e.g., `schema.proto`)
    ///      outside the database.
    ///      Example:
    ///      ```sql
    ///      CREATE TABLE messages (
    ///          id INT PRIMARY KEY,
    ///          data PROTO,  -- A column that stores Protobuf-serialized data
    ///          schema_uri STRING  -- A URI to the Protobuf schema file (e.g., "path/to/schema.proto")
    ///      );
    ///      ```
    ///      This creates a `data` column that stores Protobuf-serialized data, and the
    ///      `schema_uri` column stores the path or location of the Protobuf schema that
    ///      defines the structure of the data.
    ///
    ///   2. **Inserting Protobuf Data**:
    ///      Protobuf data must be serialized into a binary format and inserted into the `PROTO` column.
    ///      The schema is identified by the URI, and the data is encoded/decoded at the application level.
    ///      Example:
    ///      ```sql
    ///      INSERT INTO messages (id, data, schema_uri)
    ///      VALUES (1, <SerializedProtobufData>, "path/to/schema.proto");
    ///      ```
    ///      The Protobuf data is serialized externally and stored in the `data` column. The
    ///      schema for decoding is identified by the `schema_uri`.
    ///
    ///   3. **No Direct Field Access**:
    ///      SQL does **not** support direct querying or extracting fields from Protobuf data stored in `PROTO` columns.
    ///      You must deserialize the data in your application code using a Protobuf library and the schema
    ///      defined by the `schema_uri`.
    ///      Example:
    ///      ```cpp
    ///      // Example in C++ (pseudo-code):
    ///      std::string schema_uri = get_schema_uri_from_db();
    ///      std::string protobuf_data = get_protobuf_data_from_db();
    ///      MyMessage message = DeserializeProtobuf(schema_uri, protobuf_data);
    ///      std::string field_value = message.field_name();
    ///      ```
    ///
    ///   **Important Notes:**
    ///      - The Protobuf schema URI (`schema_uri`) is critical to understanding the structure of the serialized data.
    ///      - The actual structure of the Protobuf data is **not** directly available in SQL. You need to deserialize it using
    ///        the corresponding Protobuf schema outside the database, which introduces **additional complexity**.
    ///      - The current implementation avoids direct dynamic access to Protobuf fields in SQL, making it safer, but you
    ///        need to handle the schema and deserialization logic correctly in your application to prevent errors and security risks.
    ///      - Protobuf-based data storage is ideal for efficient binary serialization of structured data, especially for complex
    ///        or nested data models that might not be directly supported by SQL types.
    ////////////////////////////////////////////////////////////////////////////////
    struct TypeNameProto final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "PROTO";

        TypeNameProto() : TypeStmt(pb::ast::TYPE_NAME_PROTO) {
        }

        static TypeNameProto *make_typename(turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameProto>(arena);
            String name;
            name = TYPE_NAME;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;

        StringConstantExpr *uri{nullptr};
    };

    ////////////////////////////////////////////////////////////////////////////////
    /// TypeNameEnum - Represents the `ENUM` type in SQL, with support for explicit and
    /// implicit integer values.
    ///
    /// This class models the `ENUM` type in SQL, which is used to define a set of named values.
    /// Each `ENUM` value can have an explicitly specified integer value, or if the integer value
    /// is omitted, it will automatically increment from the last specified value.
    ///
    /// **SQL Usage:**
    ///   - **Explicit Integer Values**: You can explicitly assign integer values to specific `ENUM` values.
    ///     Example:
    ///     ```sql
    ///     CREATE TABLE products (
    ///         id INT PRIMARY KEY,
    ///         status ENUM('active' = 10, 'inactive' = 20, 'discontinued')  -- Specific integer values for some items
    ///     );
    ///     ```
    ///     In this case, `'active'` maps to `10`, `'inactive'` maps to `20`, and `'discontinued'`
    ///     automatically takes the next available value, such as `21`.
    ///
    ///   - **Implicit Integer Values**: If an integer value is omitted, the value automatically
    ///     increments from the last specified value.
    ///     Example:
    ///     ```sql
    ///     CREATE TABLE products (
    ///         id INT PRIMARY KEY,
    ///         status ENUM(a = 10, b = 20, c, d, e)  -- 'a' = 10, 'b' = 20, 'c' = 21, 'd' = 22, 'e' = 23
    ///     );
    ///     ```
    ///     Here, `a` is explicitly assigned `10`, `b` is explicitly assigned `20`, and `c`, `d`, `e`
    ///     are implicitly assigned `21`, `22`, `23` respectively.
    ///
    ///   **Key Points:**
    ///     - **Implicit Increment**: When no integer is provided, the value is automatically set
    ///       to the previous value plus one.
    ///     - **Custom Value Assignment**: The ability to assign specific integers to `ENUM` values
    ///       offers flexibility, especially when dealing with large datasets that might benefit from
    ///       predefined integer mappings.
    ///     - **Flexibility**: This allows users to define `ENUM` types with a mix of explicit and implicit values.
    ///
    ///   **Example Usage:**
    ///     1. **Explicit values**:
    ///        ```sql
    ///        CREATE TABLE example (
    ///            id INT PRIMARY KEY,
    ///            status ENUM('active' = 10, 'inactive' = 20, 'discontinued')  -- custom integer mappings
    ///        );
    ///        ```
    ///     2. **Mixed explicit and implicit values**:
    ///        ```sql
    ///        CREATE TABLE example (
    ///            id INT PRIMARY KEY,
    ///            status ENUM('active' = 10, 'inactive' = 20, 'discontinued')  -- custom integer mappings
    ///        );
    ///        ```
    ////////////////////////////////////////////////////////////////////////////////

    struct TypeNameEnum final : public TypeStmt {
        static constexpr const char *TYPE_NAME = "ENUM";

        TypeNameEnum() : TypeStmt(pb::ast::TYPE_NAME_ENUM) {
        }

        static TypeNameEnum *make_typename(turbo::Arena &arena) {
            auto ptr = alloc_type<TypeNameEnum>(arena);
            String name;
            name = TYPE_NAME;
            ptr->names.push_back(name, arena);
            return ptr;
        }

        nlohmann::ordered_json to_json() const override;

        TypeNameUri *uri{nullptr};
        Vector<EnumElement *> type_args;
    };

    //////////////////////////////////////////////////////////////////////////////
    /// StartTxnStmt - Represents a statement for starting a transaction.
    ///
    /// This struct models a `START TRANSACTION` SQL statement, which is used
    /// to initiate a transaction in a database system. It supports setting the
    /// transaction to be read-only or read-write based on the `read_only` flag.
    ///
    /// Fields:
    /// - `read_only`: Flag indicating whether the transaction should be read-only.
    ///   If set to true, the transaction will disallow write operations.
    ///
    /// Example:
    /// START TRANSACTION;  // Default, read-write
    /// START TRANSACTION READ ONLY;  // Read-only transaction
    ///
    //////////////////////////////////////////////////////////////////////////////

    struct StartTxnStmt : public StmtNode {
        StartTxnStmt() : StmtNode(pb::ast::STMT_START_TRANSACTION) {
        }

        bool read_only{false};

        nlohmann::ordered_json to_json() const override;
    };

    //////////////////////////////////////////////////////////////////////////////
    /// CommitTxnStmt - Represents a statement for committing a transaction.
    ///
    /// This struct models a `COMMIT TRANSACTION` SQL statement, which is used
    /// to finalize and persist changes made during a transaction. Once a transaction
    /// is committed, all changes are made permanent in the database.
    ///
    /// Example:
    /// COMMIT TRANSACTION;  // Commits the current transaction and makes all changes permanent
    ///
    //////////////////////////////////////////////////////////////////////////////

    struct CommitTxnStmt : public StmtNode {
        CommitTxnStmt() : StmtNode(pb::ast::STMT_COMMIT_TRANSACTION) {
        }

        nlohmann::ordered_json to_json() const override;
    };

    //////////////////////////////////////////////////////////////////////////////
    /// RollbackTxnStmt - Represents a statement for rolling back a transaction.
    ///
    /// This struct models a `ROLLBACK TRANSACTION` SQL statement, which is used
    /// to undo the changes made during the current transaction. A rollback reverts
    /// the database to its state before the transaction began, effectively discarding
    /// any uncommitted changes.
    ///
    /// Example:
    /// ROLLBACK TRANSACTION;  // Rolls back the current transaction and undoes any changes
    ///
    //////////////////////////////////////////////////////////////////////////////

    struct RollbackTxnStmt : public StmtNode {
        RollbackTxnStmt() : StmtNode(pb::ast::STMT_ROLLBACK_TRANSACTION) {
        }

        nlohmann::ordered_json to_json() const override;
    };


    //////////////////////////////////////////////////////////////////////////////
    /// PragmaSetStmt - Represents a PRAGMA SET statement, which is used for setting
    /// configuration options in SQL.
    ///
    /// This struct models the `PRAGMA SET` command, which allows you to modify
    /// specific database settings like enabling or disabling certain features.
    /// The `var_list` field holds the list of variable assignments that will be set
    /// via this PRAGMA statement.
    ///
    /// Fields:
    /// - `var_list`: A list of `VarAssign` objects, each representing a key-value
    ///   pair to be set as part of the PRAGMA command.
    ///
    /// Example:
    /// PRAGMA foreign_keys = ON, cache_size = 1000;  // Sets multiple PRAGMA options
    ///
    //////////////////////////////////////////////////////////////////////////////

    struct PragmaSetStmt : public StmtNode {
        Vector<VarAssign *> var_list;

        PragmaSetStmt() : StmtNode(pb::ast::STMT_PRAGMA_SET) {
        }

        nlohmann::ordered_json to_json() const override;
    };

    struct NewPrepareStmt : public StmtNode {
        String name;
        String sql;

        NewPrepareStmt() : StmtNode(pb::ast::STMT_NEW_PREPARE) {
            name = nullptr;
            sql = nullptr;
        }
        nlohmann::ordered_json to_json() const override;
    };

    struct ExecPrepareStmt : public StmtNode {
        String name;
        Vector<String> param_list;

        ExecPrepareStmt() : StmtNode(pb::ast::STMT_EXEC_PREPARE) {
            name = nullptr;
        }

        nlohmann::ordered_json to_json() const override;
    };

    struct DeallocPrepareStmt : public StmtNode {
        String name;

        DeallocPrepareStmt() : StmtNode(pb::ast::STMT_DEALLOC_PREPARE) {
            name = nullptr;
        }

        nlohmann::ordered_json to_json() const override;
    };

    struct KillStmt : public StmtNode {
        int64_t conn_id = 0;
        bool is_query = false;

        KillStmt() : StmtNode(pb::ast::STMT_KILL) {
        }

        nlohmann::ordered_json to_json() const override;
    };


    struct CreateNamespaceStmt : public StmtNode {
        bool if_not_exists = false;
        String ns_name;
        Vector<NamespaceOption *> options;

        CreateNamespaceStmt() : StmtNode(pb::ast::STMT_CREATE_NAMESPACE) {
            ns_name = nullptr;
        }

        nlohmann::ordered_json to_json() const override;
    };

    struct DropNamespaceStmt : public StmtNode {
        bool if_exists = false;
        String ns_name;

        DropNamespaceStmt() : StmtNode(pb::ast::STMT_DROP_NAMESPACE) {
            ns_name = nullptr;
        }

        nlohmann::ordered_json to_json() const override;
    };

    struct AlterNamespaceStmt : public StmtNode {
        bool if_exists = false;
        String ns_name;
        Vector<NamespaceOption *> options;

        AlterNamespaceStmt() : StmtNode(pb::ast::STMT_ALTER_NAMESPACE) {
            ns_name = nullptr;
        }

        nlohmann::ordered_json to_json() const override;
    };

    // CreateUserStmt creates user account.
    // See https://dev.mysql.com/doc/refman/5.7/en/create-user.html
    struct CreateUserStmt : public StmtNode {
        CreateUserStmt() : StmtNode(pb::ast::STMT_CREATE_USER) {
            namespace_name = nullptr;
        }

        nlohmann::ordered_json to_json() const override;

        bool if_not_exists = false;
        Vector<UserSpec *> specs;
        String namespace_name;
    };

    struct DropUserStmt : public StmtNode {
        DropUserStmt() : StmtNode(pb::ast::STMT_DROP_USER) {
        }

        nlohmann::ordered_json to_json() const override;

        bool if_exists = false;
        Vector<UserSpec *> specs;
    };

    struct AlterUserStmt : public StmtNode {
        AlterUserStmt() : StmtNode(pb::ast::STMT_ALTER_USER) {
            namespace_name = nullptr;
        }

        nlohmann::ordered_json to_json() const override;

        bool if_exists = false;
        Vector<UserSpec *> specs;
        String namespace_name;
    };


    struct PrivStmt : public StmtNode {
        PrivStmt(pb::ast::StmtType stmt_type) : StmtNode(stmt_type) {
            namespace_name.set_null();
        }

        nlohmann::ordered_json to_json() const override;

        Vector<PrivType *> privs;
        PrivLevel *priv_level = nullptr;
        Vector<UserSpec *> specs;
        bool with_grant = false;
        String namespace_name;

        uint32_t get_acl() const;
    };

    struct GrantStmt : public PrivStmt {
        GrantStmt() : PrivStmt(pb::ast::STMT_GRANT) {
        }

        nlohmann::ordered_json to_json() const override;
    };

    struct RevokeStmt : public PrivStmt {
        RevokeStmt() : PrivStmt(pb::ast::STMT_REVOKE) {
        }

        nlohmann::ordered_json to_json() const override;
    };


    struct InsertStmt : public StmtNode {
        pb::ast::PriorityEnum priority{pb::ast::PriorityEnum::PE_NO_PRIORITY};
        bool is_replace = false;
        bool is_merge = false;
        bool is_ignore = false;
        TableName *table_name = nullptr;
        StmtNode *subquery_stmt = nullptr;
        Vector<ColumnName *> columns;
        Vector<RowExpr *> lists;
        Vector<String> partition_names;
        Vector<Assignment *> on_duplicate;

        InsertStmt() : StmtNode(pb::ast::STMT_INSERT) {
        }


        nlohmann::ordered_json to_json() const override;

        static InsertStmt *New(turbo::Arena &arena) {
            InsertStmt *insert = new(arena.allocate(sizeof(InsertStmt)))InsertStmt();
            insert->columns.reserve(10, arena);
            insert->lists.reserve(10, arena);
            insert->on_duplicate.reserve(10, arena);
            return insert;
        }

        void to_stream(std::ostream &os) const override;
    };

    struct UpdateStmt : public StmtNode {
        pb::ast::PriorityEnum priority;
        bool is_ignore = false;
        TableRef *table_refs = nullptr; // join or tableresource
        Vector<Assignment *> set_list;
        WhereClause *where = nullptr;
        OrderByClause *order = nullptr;
        LimitClause *limit = nullptr;

        UpdateStmt() : StmtNode(pb::ast::STMT_UPDATE) {
        }

        nlohmann::ordered_json to_json() const override;

        void to_stream(std::ostream &os) const override;
    };

    struct TruncateStmt : public StmtNode {
        TableName *table_name = nullptr;
        Vector<String> partition_names;

        TruncateStmt() : StmtNode(pb::ast::STMT_TRUNCATE) {
        }

        nlohmann::ordered_json to_json() const override;

        void to_stream(std::ostream &os) const override {
            os << "TRUNCATE " << table_name;
        }
    };


    struct DeleteStmt : public StmtNode {
        pb::ast::PriorityEnum priority;
        bool is_ignore = false;
        bool is_quick = false;
        TableRef *from_table = nullptr; //table_name or join or tableresource
        TableName *table_name = nullptr;
        Vector<TableName *> delete_table_list;
        Vector<String> partition_names;
        WhereClause *where = nullptr;
        OrderByClause *order = nullptr;
        LimitClause *limit = nullptr;

        DeleteStmt() : StmtNode(pb::ast::STMT_DELETE) {
        }

        nlohmann::ordered_json to_json() const override;


        void to_stream(std::ostream &os) const;
    };


    struct SelectStmt final : public StmtNode {
        SelectStmtOpts *select_opt = nullptr;
        Vector<SelectField *> fields;
        TableRef *table_refs = nullptr;
        WhereClause *where = nullptr;
        GroupByClause *group = nullptr;
        HavingClause *having = nullptr;
        OrderByClause *order = nullptr;
        LimitClause *limit = nullptr;
        WithClause *with = nullptr;
        bool is_in_braces = false;
        pb::ast::SelectLock lock = pb::ast::SL_NONE;
        String option;

        SelectStmt() : StmtNode(pb::ast::STMT_SELECT) {
            option = nullptr;
        }

        bool is_complex_node() override;

        void to_stream(std::ostream &os) const override;

        nlohmann::ordered_json to_json() const override;
    };

    struct UnionStmt : public StmtNode {
        bool distinct = false;
        Vector<SelectStmt *> select_stmts;
        OrderByClause *order = nullptr;
        LimitClause *limit = nullptr;
        pb::ast::SelectLock lock = pb::ast::SL_NONE;
        bool is_in_braces = false;

        UnionStmt() : StmtNode(pb::ast::STMT_UNION) {
            is_complex = true;
        }

        nlohmann::ordered_json to_json() const override;

        void to_stream(std::ostream &os) const override;
    };

    struct ColumnDef;
    struct Constraint;
    struct TableOption;

    struct CreateTableStmt : public StmtNode {
        bool if_not_exist = false;
        TableName *table_name = nullptr;
        TableName *ref_table_name = nullptr;

        Vector<ColumnDef *> columns;
        Vector<Constraint *> constraints;
        Vector<TableOption *> options;

        void set_def_element(Vector<TableDefElement *> *defs, turbo::Arena &arena);

        nlohmann::ordered_json to_json() const;

        CreateTableStmt() : StmtNode(pb::ast::STMT_CREATE_TABLE) {
        }
    };

    struct DropTableStmt : public StmtNode {
        bool if_exist = false;
        Vector<TableName *> table_names;

        DropTableStmt() : StmtNode(pb::ast::STMT_DROP_TABLE) {
        }

        nlohmann::ordered_json to_json() const override;
    };

    struct CreateViewStmt : public StmtNode {
        bool or_replace = false;
        TableName *view_name = nullptr;
        Vector<ColumnName *> column_names;
        StmtNode *view_select_stmt = nullptr;

        CreateViewStmt() : StmtNode(pb::ast::STMT_CREATE_VIEW) {
        }

        nlohmann::ordered_json to_json() const override;
    };

    struct DropViewStmt : public StmtNode {
        bool if_exist = false;
        TableName *view_name = nullptr;

        DropViewStmt() : StmtNode(pb::ast::STMT_DROP_VIEW) {
        }
        nlohmann::ordered_json to_json() const override;
    };

    struct RestoreTableStmt : public StmtNode {
        Vector<TableName *> table_names;

        RestoreTableStmt() : StmtNode(pb::ast::STMT_RESTORE_TABLE) {
        }
        nlohmann::ordered_json to_json() const override;
    };

    struct DatabaseOption;

    struct CreateDatabaseStmt : public StmtNode {
        bool if_not_exist = false;
        String db_name;
        Vector<DatabaseOption *> options;

        CreateDatabaseStmt() : StmtNode(pb::ast::STMT_CREATE_DATABASE) {
            db_name = nullptr;
        }

        nlohmann::ordered_json to_json() const override;
    };

    struct DropDatabaseStmt : public StmtNode {
        bool if_exist = false;
        String db_name;

        DropDatabaseStmt() : StmtNode(pb::ast::STMT_DROP_DATABASE) {
            db_name = nullptr;
        }

        nlohmann::ordered_json to_json() const override;
    };

    struct AlterTableSpec;

    struct AlterTableStmt : public StmtNode {
        bool ignore = false;
        TableName *table_name = nullptr;
        Vector<AlterTableSpec *> alter_specs;

        nlohmann::ordered_json to_json() const override;

        AlterTableStmt() : StmtNode(pb::ast::STMT_ALTER_TABLE) {
        }
    };

    struct AlterViewStmt : public StmtNode {
        TableName *view_name = nullptr;
        StmtNode *view_select_stmt = nullptr;
        Vector<ColumnName *> column_names;

        AlterViewStmt() : StmtNode(pb::ast::STMT_ALTER_VIEW) {
        }

        nlohmann::ordered_json to_json() const override;
    };

    struct ExplainStmt : public StmtNode {
        StmtNode *stmt = nullptr;
        String format;

        ExplainStmt() : StmtNode(pb::ast::STMT_EXPLAIN) {
            format = nullptr;
        }
        nlohmann::ordered_json to_json() const override;
    };

    struct LoadDataStmt : public StmtNode {
        LoadDataStmt() : StmtNode(pb::ast::STMT_LOAD_DATA) {
            path.set_null();
            char_set.set_null();
        }

        int32_t ignore_lines = 0;
        bool is_local{false};
        String path;
        String char_set;
        FieldsClause *fields_info = nullptr;
        LinesClause *lines_info = nullptr;
        TableName *table_name = nullptr;
        pb::ast::OnDuplicateKeyHandle on_duplicate_handle = pb::ast::ON_DUPLICATE_KEY_IGNORE;
        Vector<ColumnName *> columns;
        Vector<Assignment *> set_list;

        nlohmann::ordered_json to_json() const override;


        void to_stream(std::ostream &os) const override;
    };

    struct CreateTypeStmt : public StmtNode {
        CreateTypeStmt() : StmtNode(pb::ast::STMT_CREATE_TYPE) {
            proto.set_null();
            json.set_null();
        }

        nlohmann::ordered_json to_json() const override;

        TypeStmt *type_name{nullptr};
        TypeNameUri *uri{nullptr};
        String proto;
        String json;
        bool is_try{false};
        cantor::pb::ast::TypeScope scope_type{cantor::pb::ast::TypeScope::TS_TABLE};
    };

    //
    //struct RestoreTableStmt : public DdlNode {
    //    Vector<ColumnName*> table_names;
    //
    //    RestoreTableStmt() {
    //        node_type = NT_RESTORE_TABLE;
    //    }
    //};
    /////////////////////////////////////////////////////////////////////////////////////////////
    /// inlines

    inline bool SelectStmt::is_complex_node() {
        if (is_complex) {
            return true;
        }
        if (with != nullptr && with->is_complex_node()) {
            is_complex = true;
            return true;
        }
        for (int i = 0; i < fields.size(); i++) {
            if (fields[i]->is_complex_node()) {
                is_complex = true;
                return true;
            }
        }
        if (table_refs != nullptr && table_refs->is_complex_node()) {
            is_complex = true;
            return true;
        }
        if (where != nullptr) {
            if (where->expr->expr_type == pb::ast::ENT_FUNC_EXPR) {
                auto f = static_cast<FuncExpr *>(where->expr);
                if (f->is_complex_node()) {
                    is_complex = true;
                    return true;
                }
            }
            if (where->expr->is_complex_node()) {
                is_complex = true;
                return true;
            }
        }
        if (group != nullptr && group->is_complex_node()) {
            is_complex = true;
            return true;
        }
        if (having != nullptr && having->expr->is_complex_node()) {
            is_complex = true;
            return true;
        }
        if (order != nullptr && order->is_complex_node()) {
            is_complex = true;
            return true;
        }
        if (limit != nullptr && limit->is_complex_node()) {
            is_complex = true;
            return true;
        }
        return false;
    }


    inline uint32_t PrivStmt::get_acl() const {
        uint32_t acl = 0;
        for (int32_t i = 0; i < privs.size(); i++) {
            PrivType *priv = (privs)[i];
            acl |= priv->get_priv_acl(priv_level->level);
        }
        if (with_grant) {
            acl |= GRANT_ACL;
        }
        return acl;
    }

    inline bool is_union(Node *node) {
        if (!node) return false;
        if (node->node_type != pb::ast::NODE_TYPE_STMT) {
            return false;
        }
        auto ddl = static_cast<const StmtNode *>(node);
        if (ddl->stmt_type != pb::ast::STMT_UNION) {
            return false;
        }
        return true;
    }

    inline bool is_union(StmtNode *node) {
        if (!node) return false;
        if (node->stmt_type != pb::ast::STMT_UNION) {
            return false;
        }
        return true;
    }

    inline bool is_select(Node *node) {
        if (!node) return false;
        if (node->node_type != pb::ast::NODE_TYPE_STMT) {
            return false;
        }
        auto ddl = static_cast<const StmtNode *>(node);
        if (ddl->stmt_type != pb::ast::STMT_SELECT) {
            return false;
        }
        return true;
    }

    inline bool is_select(StmtNode *node) {
        if (!node) return false;
        if (node->stmt_type != pb::ast::STMT_SELECT) {
            return false;
        }
        return true;
    }


} // namespace cantor::parser
